import { Injectable } from '@nestjs/common';
import type { Schedule } from '@prisma/client';
import { Prisma } from '@prisma/client';

import { PrismaService } from '../../../prisma/prisma.service';
import { NotificationModule } from '../../../types/dto';
import { ErrorCode } from '../../../types/response';
import { BusinessException } from '../../common/exceptions/businessException';
import { WinstonLoggerService } from '../../common/services/winston-logger.service';
import {
  isFullPermissionRole,
  isRestrictedRole,
} from '../../common/utils/auth.utils';
import dayjs from '../../common/utils/date-time.utils';
import { NotificationService } from '../../commonModules/notification/notification.service';
import {
  CreateCommentDto,
  CreateScheduleDto,
  DeleteScheduleDto,
  ScheduleCalendarDataDto,
  ScheduleCalendarQueryDto,
  ScheduleDetailQueryDto,
  ScheduleDetailResDto,
  ScheduleListDto,
  ScheduleListResDto,
  ScheduleStatisticsDto,
  UpdateScheduleDto,
  UpdateScheduleStatusDto,
} from './schedule.dto';

@Injectable()
export class ScheduleService {
  constructor(
    private readonly prisma: PrismaService,
    private readonly logger: WinstonLoggerService,
    private readonly notificationService: NotificationService,
  ) {}

  /**
   * 根据ID查询日程，不存在则抛出异常
   * @param id 日程ID
   * @returns 日程实体
   */
  private async findScheduleByIdOrFail(id: string): Promise<Schedule> {
    const schedule = await this.prisma.schedule.findFirst({
      where: {
        id,
        delete: 0,
      },
    });

    if (!schedule) {
      throw new BusinessException(ErrorCode.TASK_NOT_FOUND, '日程不存在');
    }

    return schedule;
  }

  /**
   * 根据ID查询日程详情（包含关联数据），不存在则抛出异常
   * @param id 日程ID
   * @returns 完整的日程信息
   */
  private async findScheduleDetailByIdOrFail(id: string): Promise<
    Prisma.ScheduleGetPayload<{
      include: {
        creator: {
          select: {
            id: true;
            name: true;
            code: true;
            department: true;
          };
        };
        comments: {
          include: {
            user: {
              select: {
                id: true;
                name: true;
                code: true;
                department: true;
                role: { select: { name: true } };
              };
            };
          };
        };
      };
    }>
  > {
    const schedule = await this.prisma.schedule.findFirst({
      where: {
        id,
        delete: 0,
      },
      include: {
        creator: {
          select: {
            id: true,
            name: true,
            code: true,
            department: true,
          },
        },
        comments: {
          where: { delete: 0 },
          include: {
            user: {
              select: {
                id: true,
                name: true,
                code: true,
                department: true,
                role: {
                  select: {
                    name: true,
                  },
                },
              },
            },
          },
          orderBy: { createTime: 'asc' },
        },
      },
    });

    if (!schedule) {
      throw new BusinessException(ErrorCode.TASK_NOT_FOUND, '日程不存在');
    }

    return schedule;
  }

  /**
   * 获取日程列表，根据角色过滤
   */
  async getScheduleList(
    userId: string,
    roleName: string,
    dto: ScheduleListDto,
  ): Promise<ScheduleListResDto> {
    this.logger.log(`[操作] 获取日程列表 - 用户: ${userId}, 角色: ${roleName}`);

    try {
      // 构建查询条件（可读性优化，不改变原有业务逻辑）
      // 约定：
      // - permissionOr：权限放宽条件（仅受限角色使用），最终与其他条件以 AND 组合
      // - orGroups：需要并列为 OR 的条件组（如关键词、执行人）；当存在多个组时，需要以 AND 连接不同的 OR 组（交集）
      // - andClauses：必须满足的 AND 条件（如状态、日期、权限放宽后的权限限制）
      const where: Record<string, unknown> = { delete: 0 };
      const andClauses: Array<Record<string, unknown>> = [];
      let pendingOrGroup: Array<Record<string, unknown>> | null = null; // 存储第一个 OR 组；若出现第二个 OR 组，则将两者转为 AND([OR(a)], [OR(b)])

      // 步骤1：根据角色构建权限条件
      let sameRoleUserIds: string[] = [];
      const fullPermission = isFullPermissionRole(roleName);
      const restricted = isRestrictedRole(roleName);

      if (!fullPermission) {
        // 受限角色：需要限制数据可见范围
        // 1) 尝试获取当前用户的角色ID
        const currentUser = await this.prisma.user.findUnique({
          where: { id: userId },
          select: { roleId: true },
        });

        let permissionOr: Array<Record<string, unknown>>;
        if (currentUser?.roleId) {
          // 2) 获取同角色所有用户ID
          const sameRoleUsers = await this.prisma.user.findMany({
            where: { roleId: currentUser.roleId, delete: 0 },
            select: { id: true },
          });
          sameRoleUserIds = sameRoleUsers.map((u) => u.id);

          // 3) 权限放宽 OR 条件（包含所有可能相关的数据）
          permissionOr = [
            // 条件1：非私有 + 创建者是同角色
            { isPrivate: false, creatorId: { in: sameRoleUserIds } },
            // 条件2：私有 + 创建者是自己
            { isPrivate: true, creatorId: userId },
            // 条件3：执行人包含自己（JSON 搜索）
            {
              assignedUserIds: {
                path: '$',
                string_contains: `"${userId}"`,
              },
            },
          ];
        } else {
          // 无角色用户：仅查看与自己相关的数据
          permissionOr = [
            { creatorId: userId },
            { assignedUserIds: { path: '$', string_contains: `"${userId}"` } },
          ];
        }

        // 将权限 OR 条件与其他条件以 AND 组合（不直接赋给 where.OR，便于统一组合顺序）
        andClauses.push({ OR: permissionOr });
      }

      // 步骤2：状态筛选（恒为 AND 条件）
      if (dto.status) {
        andClauses.push({ status: dto.status });
      }

      // 步骤3：关键词搜索（一个 OR 组）
      if (dto.keyword) {
        const keywordOr = [
          { title: { contains: dto.keyword } },
          { description: { contains: dto.keyword } },
        ];
        if (restricted || fullPermission) {
          // 行为保持一致：
          // - 若此前无 OR 组，暂存为 pendingOrGroup
          // - 若已有 OR 组，将两者以 AND 连接（交集）
          if (!pendingOrGroup) {
            pendingOrGroup = keywordOr;
          } else {
            andClauses.push({ OR: pendingOrGroup }, { OR: keywordOr });
            pendingOrGroup = null;
          }
        }
      }

      // 步骤4：执行人搜索（一个 OR 组）
      if (dto.executorIds && dto.executorIds.length > 0) {
        const executorOr = dto.executorIds.map((executorId) => ({
          assignedUserIds: { path: '$', string_contains: `"${executorId}"` },
        }));
        if (!pendingOrGroup) {
          pendingOrGroup = executorOr;
        } else {
          andClauses.push({ OR: pendingOrGroup }, { OR: executorOr });
          pendingOrGroup = null;
        }
      }

      // 步骤4.5：日期筛选（优先按 executeDate，当 executeDate 为空时按 createTime）
      if (dto.date) {
        // 日期格式：YYYY-MM-DD，当天区间 [00:00:00, 23:59:59.999]
        const selectedDate = new Date(dto.date);
        const startOfDay = new Date(selectedDate);
        startOfDay.setHours(0, 0, 0, 0);
        const endOfDay = new Date(selectedDate);
        endOfDay.setHours(23, 59, 59, 999);

        // 期望的业务条件：
        // 1) executeDate 在当天范围内
        // 2) 或 executeDate 为空 且 createTime 在当天范围内
        const dateCondition = {
          OR: [
            {
              executeDate: {
                gte: startOfDay,
                lte: endOfDay,
              },
            },
            {
              AND: [
                { executeDate: null },
                {
                  createTime: {
                    gte: startOfDay,
                    lte: endOfDay,
                  },
                },
              ],
            },
          ],
        } as const;
        // 行为对齐：
        // - 若已有 AND 条件，则追加
        // - 若存在挂起的 OR 组，则与日期条件共同并入 AND
        // - 若两者都没有，则仅使用日期条件
        if (andClauses.length > 0) {
          andClauses.push(dateCondition as unknown as Record<string, unknown>);
        } else if (pendingOrGroup) {
          andClauses.push(
            { OR: pendingOrGroup },
            dateCondition as unknown as Record<string, unknown>,
          );
          pendingOrGroup = null;
        } else {
          Object.assign(where, dateCondition);
        }
      }

      // 将 pendingOrGroup 与 andClauses 统一合并到 where
      type LogicalWhere = {
        AND?: Array<Record<string, unknown>>;
        OR?: Array<Record<string, unknown>>;
      };
      const wf = where as Record<string, unknown> & LogicalWhere;
      if (pendingOrGroup) {
        if (andClauses.length > 0) {
          wf.AND = [{ OR: pendingOrGroup }, ...andClauses];
        } else {
          wf.OR = pendingOrGroup;
        }
        pendingOrGroup = null;
      } else if (andClauses.length > 0) {
        wf.AND = andClauses;
      }

      // 步骤5：查询所有数据（不分页）
      const allSchedules = await this.prisma.schedule.findMany({
        where:
          wf as unknown as import('@prisma/client').Prisma.ScheduleWhereInput,
        include: {
          creator: {
            select: {
              id: true,
              name: true,
              code: true,
              department: true,
            },
          },
          comments: {
            where: { delete: 0 },
            include: {
              user: {
                select: {
                  id: true,
                  name: true,
                  code: true,
                  department: true,
                  role: {
                    select: {
                      name: true,
                    },
                  },
                },
              },
            },
            orderBy: { createTime: 'asc' },
          },
        },
        orderBy: { createTime: 'desc' },
      });

      // 步骤5: 应用层精确过滤（针对非私有日程的执行人员）
      let filteredSchedules = allSchedules;

      if (!isFullPermissionRole(roleName) && sameRoleUserIds.length > 0) {
        filteredSchedules = allSchedules.filter((schedule) => {
          const assignedUserIds = JSON.parse(
            schedule.assignedUserIds as string,
          ) as string[];

          // 私有日程：必须是创建者或执行人员
          if (schedule.isPrivate) {
            return (
              schedule.creatorId === userId || assignedUserIds.includes(userId)
            );
          }

          // 非私有日程：创建者或执行人员是同角色用户
          const hasAssignedFromSameRole = assignedUserIds.some((id) =>
            sameRoleUserIds.includes(id),
          );

          return (
            sameRoleUserIds.includes(schedule.creatorId) ||
            hasAssignedFromSameRole
          );
        });
      }

      // 步骤6: 手动分页
      const page = dto.page || 1;
      const pageSize = dto.pageSize || 10;
      const total = filteredSchedules.length;
      const startIndex = (page - 1) * pageSize;
      const endIndex = startIndex + pageSize;
      const paginatedSchedules = filteredSchedules.slice(startIndex, endIndex);

      // 步骤7: 获取关联用户信息
      const scheduleList = await Promise.all(
        paginatedSchedules.map(async (schedule) => {
          const assignedUserIds = JSON.parse(
            schedule.assignedUserIds as string,
          ) as string[];
          const assignedUsers = await this.prisma.user.findMany({
            where: {
              id: { in: assignedUserIds },
              delete: 0,
            },
            select: {
              id: true,
              name: true,
              code: true,
              department: true,
            },
          });

          // executeDate业务兼容：优先返回YYYY-MM-DD格式executeDate，无则用createTime
          let execDateStr: string | null = null;
          if (schedule.executeDate) {
            execDateStr = dayjs(schedule.executeDate).format('YYYY-MM-DD');
          } else if (schedule.createTime) {
            execDateStr = dayjs(schedule.createTime).format('YYYY-MM-DD');
          }

          return {
            id: schedule.id,
            title: schedule.title,
            description: schedule.description,
            status: schedule.status,
            creatorId: schedule.creatorId,
            creator: schedule.creator,
            assignedUserIds,
            assignedUsers,
            companyId: schedule.companyId,
            isPrivate: schedule.isPrivate,
            createTime: schedule.createTime,
            updateTime: schedule.updateTime,
            comments: schedule.comments.map((comment) => ({
              id: comment.id,
              content: comment.content,
              createTime: comment.createTime,
              updateTime: comment.updateTime,
              user: comment.user,
            })),
            executeDate: execDateStr,
          };
        }),
      );

      this.logger.log(
        `[操作] 获取日程列表成功 - 共 ${total} 个日程，返回第 ${page} 页`,
      );
      return scheduleList;
    } catch (error) {
      this.logger.error(
        `[失败] 获取日程列表 - ${error instanceof Error ? error.message : '未知错误'}`,
        error instanceof Error ? error.stack : undefined,
      );
      throw error;
    }
  }

  /**
   * 获取日程详情
   */
  async getScheduleDetail(
    userId: string,
    roleName: string,
    dto: ScheduleDetailQueryDto,
  ): Promise<ScheduleDetailResDto> {
    this.logger.log(`[操作] 获取日程详情 - ID: ${dto.id}, 用户: ${userId}`);

    try {
      // 获取详细的日程信息（包含关联数据）
      const scheduleDetail = await this.findScheduleDetailByIdOrFail(dto.id);

      // 获取关联用户信息
      const assignedUserIds = JSON.parse(
        scheduleDetail.assignedUserIds as string,
      ) as string[];
      const assignedUsers = await this.prisma.user.findMany({
        where: {
          id: { in: assignedUserIds },
          delete: 0,
        },
        select: {
          id: true,
          name: true,
          code: true,
          department: true,
        },
      });

      // executeDate业务兼容：优先返回YYYY-MM-DD格式executeDate，无则用createTime
      const execDateStr = scheduleDetail.executeDate
        ? dayjs(scheduleDetail.executeDate).format('YYYY-MM-DD')
        : dayjs(scheduleDetail.createTime).format('YYYY-MM-DD');
      const result = {
        id: scheduleDetail.id,
        title: scheduleDetail.title,
        description: scheduleDetail.description,
        status: scheduleDetail.status,
        creatorId: scheduleDetail.creatorId,
        creator: scheduleDetail.creator,
        assignedUserIds,
        assignedUsers,
        companyId: scheduleDetail.companyId,
        isPrivate: scheduleDetail.isPrivate,
        createTime: scheduleDetail.createTime,
        updateTime: scheduleDetail.updateTime,
        comments: scheduleDetail.comments.map((comment) => ({
          id: comment.id,
          content: comment.content,
          createTime: comment.createTime,
          updateTime: comment.updateTime,
          user: comment.user,
        })),
        executeDate: execDateStr,
      };

      this.logger.log(`[操作] 获取日程详情成功 - ID: ${dto.id}`);
      return result;
    } catch (error) {
      if (error instanceof BusinessException) {
        throw error;
      }
      this.logger.error(
        `[失败] 获取日程详情 - ${error instanceof Error ? error.message : '未知错误'}`,
        error instanceof Error ? error.stack : undefined,
      );
      throw error;
    }
  }

  /**
   * 创建日程
   */
  async createSchedule(userId: string, dto: CreateScheduleDto) {
    this.logger.log(`[操作] 创建日程 - 标题: ${dto.title}, 创建人: ${userId}`);

    try {
      const schedule = await this.prisma.schedule.create({
        data: {
          title: dto.title,
          description: dto.description ?? null,
          creatorId: userId,
          assignedUserIds: JSON.stringify(dto.assignedUserIds),
          companyId: dto.companyId,
          isPrivate: dto.isPrivate ?? false,
          // 新增逻辑：如填写则保存日期，否则为 null
          executeDate: dto.executeDate ? dayjs(dto.executeDate).toDate() : null,
        },
      });

      // 创建通知
      if (dto.assignedUserIds.length > 0) {
        await this.notificationService.createScheduleAssignNotification(
          schedule.id,
          userId,
          dto.assignedUserIds,
          'create', // 创建操作
        );
      }

      this.logger.log(
        `[操作] 创建日程成功 - ID: ${schedule.id}, 标题: ${dto.title}`,
      );
      return schedule;
    } catch (error) {
      this.logger.error(
        `[失败] 创建日程 - ${error instanceof Error ? error.message : '未知错误'}`,
        error instanceof Error ? error.stack : undefined,
      );
      throw error;
    }
  }

  /**
   * 更新日程状态
   */
  async updateScheduleStatus(
    userId: string,
    roleName: string,
    dto: UpdateScheduleStatusDto,
  ) {
    this.logger.log(`[操作] 更新日程 - ID: ${dto.id}, 用户: ${userId}`);

    try {
      // 验证ID非空
      if (!dto.id) {
        throw new BusinessException(ErrorCode.TASK_NOT_FOUND, '日程ID不能为空');
      }

      // 获取日程实体用于权限验证和状态更新
      const schedule = await this.findScheduleByIdOrFail(dto.id);

      // 特殊权限检查：如果当前状态是"已完成"或"已停止"，只允许全权限角色修改
      const isFinishedStatus =
        schedule.status === '已完成' || schedule.status === '已停止';
      if (isFinishedStatus && !isFullPermissionRole(roleName)) {
        this.logger.warn(
          `[权限拒绝] 修改已完成/已停止日程 - 用户 ${userId} 无权限修改日程 ${dto.id}`,
        );
        throw new BusinessException(
          ErrorCode.FORBIDDEN,
          '仅系统管理员和公司管理者可以修改已完成/已停止的日程',
        );
      }

      // 检查权限：系统管理员和公司管理者可以修改所有日程，其他角色只能修改与自己关联的日程
      if (!isFullPermissionRole(roleName)) {
        // 检查是否为创建者
        const isCreator = schedule.creatorId === userId;

        // 检查是否为关联人员
        const assignedUserIds = JSON.parse(
          schedule.assignedUserIds as string,
        ) as string[];
        const isAssignedUser = assignedUserIds.includes(userId);

        if (!isCreator && !isAssignedUser) {
          this.logger.warn(
            `[权限拒绝] 更新日程 - 用户 ${userId} 无权限修改日程 ${dto.id}`,
          );
          throw new BusinessException(
            ErrorCode.TASK_NO_PERMISSION,
            '无权限修改该日程',
          );
        }
      }

      const updatedSchedule = await this.prisma.schedule.update({
        where: { id: dto.id },
        data: {
          status: dto.status,
        },
      });

      // 自动创建状态更新回复
      await this.createStatusUpdateComment(
        dto.id,
        userId,
        schedule.status,
        dto.status,
        dto.reason, // 传递停止原因
      );

      // 创建状态更新通知
      const assignedUserIds = JSON.parse(
        updatedSchedule.assignedUserIds as string,
      ) as string[];
      if (assignedUserIds.length > 0) {
        await this.notificationService.createScheduleAssignNotification(
          dto.id,
          updatedSchedule.creatorId, // 创建者ID
          assignedUserIds,
          'update', // 更新操作
          userId, // 更新者ID
        );
      }

      this.logger.log(`[操作] 更新日程成功 - ID: ${dto.id}`);
      return updatedSchedule;
    } catch (error) {
      if (error instanceof BusinessException) {
        throw error;
      }
      this.logger.error(
        `[失败] 更新日程 - ${error instanceof Error ? error.message : '未知错误'}`,
        error instanceof Error ? error.stack : undefined,
      );
      throw error;
    }
  }

  /**
   * 删除日程（软删除）
   */
  async deleteSchedule(
    userId: string,
    roleName: string,
    dto: DeleteScheduleDto,
  ) {
    this.logger.log(`[操作] 删除日程 - ID: ${dto.id}, 用户: ${userId}`);

    try {
      // 权限检查：只允许系统管理员和公司管理者删除
      if (!isFullPermissionRole(roleName)) {
        this.logger.warn(
          `[权限拒绝] 删除日程 - 用户 ${userId} 无权限删除日程 ${dto.id}`,
        );
        throw new BusinessException(
          ErrorCode.FORBIDDEN,
          '仅系统管理员和公司管理者可以删除日程',
        );
      }

      // 验证日程存在性
      await this.findScheduleByIdOrFail(dto.id);

      // 软删除日程
      await this.prisma.schedule.update({
        where: { id: dto.id },
        data: { delete: 1 },
      });

      // 软删除相关通知
      await this.prisma.notification.updateMany({
        where: {
          relatedId: dto.id,
          module: NotificationModule.SCHEDULE,
          delete: 0,
        },
        data: { delete: 1 },
      });

      this.logger.log(`[操作] 删除日程成功 - ID: ${dto.id}`);
      return true;
    } catch (error) {
      if (error instanceof BusinessException) {
        throw error;
      }
      this.logger.error(
        `[失败] 删除日程 - ${error instanceof Error ? error.message : '未知错误'}`,
        error instanceof Error ? error.stack : undefined,
      );
      throw error;
    }
  }

  /**
   * 添加回复
   */
  async createComment(userId: string, dto: CreateCommentDto) {
    this.logger.log(
      `[操作] 添加回复 - 日程ID: ${dto.scheduleId}, 用户: ${userId}`,
    );

    try {
      // 检查日程是否存在且有权限访问
      const schedule = await this.findScheduleByIdOrFail(dto.scheduleId);

      // 获取用户的角色信息
      const user = await this.prisma.user.findUnique({
        where: { id: userId },
        select: { role: { select: { name: true } } },
      });

      // 如果回复者既不是系统管理员也不是公司管理者，且状态不是已完成/已停止，则自动更新为待审核
      const roleName = user?.role?.name || '';
      const isFinishedStatus =
        schedule.status === '已完成' || schedule.status === '已停止';

      if (!isFullPermissionRole(roleName) && !isFinishedStatus) {
        const oldStatus = schedule.status;
        await this.prisma.schedule.update({
          where: { id: dto.scheduleId },
          data: { status: '待审核' },
        });

        // 使用统一的状态更新回复方法，传递用户回复内容作为reason
        await this.createStatusUpdateComment(
          dto.scheduleId,
          userId,
          oldStatus,
          '待审核',
          dto.content, // 将用户回复内容作为reason传递
        );

        this.logger.log(
          `[操作] 自动更新状态为待审核 - 日程ID: ${dto.scheduleId}`,
        );
      } else {
        // 如果不需要自动更新状态，则创建普通回复
        await this.prisma.scheduleComment.create({
          data: {
            scheduleId: dto.scheduleId,
            userId: userId,
            content: dto.content,
          },
        });
      }

      // 创建回复通知
      const assignedUserIds = JSON.parse(
        schedule.assignedUserIds as string,
      ) as string[];
      if (assignedUserIds.length > 0) {
        await this.notificationService.createScheduleAssignNotification(
          dto.scheduleId,
          schedule.creatorId, // 创建者ID
          assignedUserIds,
          'comment', // 回复操作
          userId, // 回复者ID
        );
      }

      this.logger.log(`[操作] 添加回复成功 - 日程ID: ${dto.scheduleId}`);
      return { success: true };
    } catch (error) {
      if (error instanceof BusinessException) {
        throw error;
      }
      this.logger.error(
        `[失败] 添加回复 - ${error instanceof Error ? error.message : '未知错误'}`,
        error instanceof Error ? error.stack : undefined,
      );
      throw error;
    }
  }

  /**
   * 检查用户是否有权限访问日程
   */
  private checkSchedulePermission(
    userId: string,
    roleName: string,
    schedule: { creatorId: string; assignedUserIds: string },
  ): boolean {
    // admin 和 boss 可以访问所有日程
    if (isFullPermissionRole(roleName)) {
      return true;
    }

    // 其他角色都执行相同的权限检查
    const assignedUserIds = JSON.parse(schedule.assignedUserIds) as string[];
    return schedule.creatorId === userId || assignedUserIds.includes(userId);
  }

  /**
   * 创建状态更新回复
   */
  private async createStatusUpdateComment(
    scheduleId: string,
    userId: string,
    oldStatus: string,
    newStatus: string,
    reason?: string, // 停止原因（可选）
  ): Promise<void> {
    try {
      // 构建回复内容
      let content = `状态已从"${oldStatus}"更新为"${newStatus}"`;

      // 如果是修改为"已停止"且有提供原因，则附加原因信息
      if (newStatus === '已停止' && reason) {
        content = `${content}，原因：${reason}`;
      }

      // 如果是修改为"待审核"且有提供原因（用户回复内容），则附加原因信息
      if (newStatus === '待审核' && reason) {
        content = `${reason}\n\n${content}`;
      }

      await this.prisma.scheduleComment.create({
        data: {
          scheduleId,
          userId,
          content,
        },
      });

      this.logger.log(
        `[操作] 自动创建状态更新回复 - 日程ID: ${scheduleId}, 用户: ${userId}`,
      );
    } catch (error) {
      this.logger.error(
        `[失败] 创建状态更新回复 - ${error instanceof Error ? error.message : '未知错误'}`,
        error instanceof Error ? error.stack : undefined,
      );
      // 不抛出错误，避免影响状态更新主流程
    }
  }

  /**
   * 获取日程统计数据
   */
  async getStatistics(
    userId: string,
    roleName: string,
  ): Promise<ScheduleStatisticsDto> {
    this.logger.log(
      `[操作] 获取日程统计数据 - 用户: ${userId}, 角色: ${roleName}`,
    );

    try {
      // 构建查询条件
      const where: Record<string, unknown> = { delete: 0 };

      // 根据角色过滤数据
      if (isFullPermissionRole(roleName)) {
        // 全权限角色：可以查看所有日程
      } else {
        // 受限角色：只能查看与自己关联的日程
        where.OR = [
          { creatorId: userId },
          { assignedUserIds: { path: '$', string_contains: `"${userId}"` } },
        ];
      }

      // 查询计划中数量
      const pendingCount = await this.prisma.schedule.count({
        where: {
          ...where,
          status: '计划中',
        },
      });

      // 查询进行中数量
      const inProgressCount = await this.prisma.schedule.count({
        where: {
          ...where,
          status: '进行中',
        },
      });

      // 查询待审核数量
      const atRiskCount = await this.prisma.schedule.count({
        where: {
          ...where,
          status: '待审核',
        },
      });

      const statistics = {
        pending: pendingCount,
        inProgress: inProgressCount,
        atRisk: atRiskCount,
      };

      this.logger.log(
        `[操作] 获取日程统计数据成功 - 计划中: ${pendingCount}, 进行中: ${inProgressCount}, 待审核: ${atRiskCount}`,
      );
      return statistics;
    } catch (error) {
      this.logger.error(
        `[失败] 获取日程统计数据 - ${error instanceof Error ? error.message : '未知错误'}`,
        error instanceof Error ? error.stack : undefined,
      );
      throw error;
    }
  }

  /**
   * 更新日程（仅编辑标题）
   */
  async updateSchedule(
    userId: string,
    roleName: string,
    dto: UpdateScheduleDto,
  ) {
    this.logger.log(`[操作] 更新日程 - ID: ${dto.id}, 用户: ${userId}`);

    try {
      // 验证日程是否存在
      const schedule = await this.findScheduleByIdOrFail(dto.id);

      // 权限检查：系统管理员和公司管理者可以修改所有日程，其他角色只能修改与自己关联的日程
      if (!isFullPermissionRole(roleName)) {
        // 检查是否为创建者
        const isCreator = schedule.creatorId === userId;

        // 检查是否为关联人员
        const assignedUserIds = JSON.parse(
          schedule.assignedUserIds as string,
        ) as string[];
        const isAssignedUser = assignedUserIds.includes(userId);

        if (!isCreator && !isAssignedUser) {
          this.logger.warn(
            `[权限拒绝] 更新日程 - 用户 ${userId} 无权限修改日程 ${dto.id}`,
          );
          throw new BusinessException(
            ErrorCode.TASK_NO_PERMISSION,
            '无权限修改该日程',
          );
        }
      }

      const updatedSchedule = await this.prisma.schedule.update({
        where: { id: dto.id },
        data: {
          title: dto.title,
        },
      });

      this.logger.log(`[操作] 更新日程成功 - ID: ${dto.id}`);
      return updatedSchedule;
    } catch (error) {
      if (error instanceof BusinessException) {
        throw error;
      }
      this.logger.error(
        `[失败] 更新日程 - ${error instanceof Error ? error.message : '未知错误'}`,
        error instanceof Error ? error.stack : undefined,
      );
      throw error;
    }
  }

  /**
   * 获取日历数据
   * 根据日期范围查询当前用户作为执行人的日程，按日期和状态分组统计
   * @param userId 用户ID
   * @param dto 查询参数（日期范围）
   * @returns 日历数据
   */
  async getCalendarData(
    userId: string,
    dto: ScheduleCalendarQueryDto,
  ): Promise<ScheduleCalendarDataDto> {
    try {
      // 构建查询的起始和结束时间
      const startDate = new Date(dto.startDate + 'T00:00:00');
      const endDate = new Date(dto.endDate + 'T23:59:59');

      // 数据权限与 getScheduleList 对齐
      // - 全权限角色：可查看所有日程
      // - 受限角色：满足以下任一条件
      //   1) 非私有 + 创建者为同角色用户
      //   2) 私有 + 创建者为自己
      //   3) 执行人包含自己
      const fullPermission = isFullPermissionRole(
        (
          await this.prisma.user.findUnique({
            where: { id: userId },
            select: { role: { select: { name: true } } },
          })
        )?.role?.name || '',
      );

      const permissionAND: Array<Record<string, unknown>> = [];
      if (!fullPermission) {
        // 获取当前用户的角色ID
        const currentUser = await this.prisma.user.findUnique({
          where: { id: userId },
          select: { roleId: true },
        });

        let permissionOr: Array<Record<string, unknown>>;
        if (currentUser?.roleId) {
          // 获取同角色所有用户ID
          const sameRoleUsers = await this.prisma.user.findMany({
            where: { roleId: currentUser.roleId, delete: 0 },
            select: { id: true },
          });
          const sameRoleUserIds = sameRoleUsers.map((u) => u.id);

          permissionOr = [
            { isPrivate: false, creatorId: { in: sameRoleUserIds } },
            { isPrivate: true, creatorId: userId },
            { assignedUserIds: { path: '$', string_contains: `"${userId}"` } },
          ];
        } else {
          // 无角色：仅限与自己相关
          permissionOr = [
            { creatorId: userId },
            { assignedUserIds: { path: '$', string_contains: `"${userId}"` } },
          ];
        }
        permissionAND.push({ OR: permissionOr });
      }

      // 查询指定日期范围内所有符合条件的日程
      const schedules = await this.prisma.schedule.findMany({
        where: {
          delete: 0,
          ...(permissionAND.length > 0 ? { AND: permissionAND } : {}),
          status: { in: ['计划中', '进行中', '待审核'] },
          createTime: { gte: startDate, lte: endDate },
        },
        select: { status: true, createTime: true, executeDate: true },
      });

      // 按日期和状态分组统计
      const calendarMap = new Map<
        string,
        { planning: number; inProgress: number; pendingReview: number }
      >();

      // 获取日历数据时的 dateStr 优先使用 executeDate，无则用 createTime
      schedules.forEach((schedule) => {
        // 兼容处理：优先用 executeDate，无则用 createTime
        let dateStr = '';
        if (schedule.executeDate) {
          dateStr = dayjs(schedule.executeDate).format('YYYY-MM-DD');
        } else {
          dateStr = dayjs(schedule.createTime).format('YYYY-MM-DD');
        }

        // 初始化该日期的统计对象
        if (!calendarMap.has(dateStr)) {
          calendarMap.set(dateStr, {
            planning: 0,
            inProgress: 0,
            pendingReview: 0,
          });
        }

        // 根据状态累加计数
        const stats = calendarMap.get(dateStr)!;
        if (schedule.status === '计划中') {
          stats.planning++;
        } else if (schedule.status === '进行中') {
          stats.inProgress++;
        } else if (schedule.status === '待审核') {
          stats.pendingReview++;
        }
      });

      // 转换为数组格式
      const calendarData: ScheduleCalendarDataDto = Array.from(
        calendarMap.entries(),
      ).map(([date, stats]) => ({
        date,
        planning: stats.planning,
        inProgress: stats.inProgress,
        pendingReview: stats.pendingReview,
      }));

      this.logger.log(
        `[操作] 获取日历数据成功 - 日期范围: ${dto.startDate} 至 ${dto.endDate}, 数据条数: ${calendarData.length}`,
      );

      return calendarData;
    } catch (error) {
      this.logger.error(
        `[失败] 获取日历数据 - ${error instanceof Error ? error.message : '未知错误'}`,
        error instanceof Error ? error.stack : undefined,
      );
      throw error;
    }
  }
}
